#pragma once
#include <iostream>
#include <unordered_map>
#include <vector>
#include <cstring>
#include <cassert>
#include "Log.hpp"
#include "Channel.hpp"
class EventLoop;//EventLoop类的声明
static const int maxEpollEvents = 1024;//epoll文件描述符数组的最大值-1
class Poller
{
public:
    Poller()
    {
        _epfd = epoll_create(1);
        if (_epfd < 0)//epoll创建失败
        {
            ERROR_LOG("EPOLL CREATE FAILED!!");
            abort();//给自己发送信号，退出进程
        }
    }
public:
    //添加或修改监控(不存在则添加，存在则更新)
    void UpdateEvent(Channel* channel)
    {
        bool ret = HasChannel(channel);//先判断某个Channel是否已经添加了事件监控
        if (ret == false)//不存在则添加
        { 
            _channels.insert(std::make_pair(channel->GetFd(), channel));//更新unordered_map
            Update(channel, EPOLL_CTL_ADD); 
        }
        else { Update(channel, EPOLL_CTL_MOD); }//存在，则根据想要关注的事件进行更新
    }
    //移除监控
    void RemoveEvent(Channel* channel)
    {
        auto it = _channels.find(channel->GetFd());//查找文件描述符是否在unordered_map中
        if(it != _channels.end())//找到了
        {
            _channels.erase(it);
        }
        Update(channel, EPOLL_CTL_DEL);//在红黑树中删除节点
    }
    //开始监控，返回活跃的channel连接数组
    void Poll(std::vector<Channel*>* active)
    {
        int nfds = epoll_wait(_epfd, _evs, maxEpollEvents, -1);//阻塞监控
        if (nfds < 0)//等待失败
        {
            if (errno == EINTR) { return; }//被信号打断，属于正常现象
            ERROR_LOG("EPOLL WAIT ERROR:%s", strerror(errno));
            abort();//退出程序
        }
        for (int i = 0; i < nfds; ++i)//遍历触发的事件
        {
            auto it = _channels.find(_evs[i].data.fd);
            assert(it != _channels.end());//肯定能找到，找不到就是管理出错了
            it->second->SetREvents(_evs[i].events);//设置实际触发的事件
            active->push_back(it->second);
        }
    }
private:
    //实际对epoll的更新操作
    void Update(Channel* channel, int op)
    {
        int fd = channel->GetFd();//从channel中获取文件描述符
        struct epoll_event ev;//创建一个epoll事件结构体
        ev.data.fd = fd;//要监控的文件描述符
        ev.events = channel->Events();//获取想要监控的事件
        // 调用 epoll_ctl 函数来添加或修改监控事件,_epfd 是 epoll 句柄，op 是操作类型，fd 是要监控的文件描述符，&ev 是事件结构体
        int ret = epoll_ctl(_epfd, op, fd, &ev); 
        if (ret < 0) { ERROR_LOG("EPOLLCTL FAILED!"); }
    }
    //判断某个Channel是否已经添加了事件监控
    bool HasChannel(Channel* channel)
    {
        auto it = _channels.find(channel->GetFd());//查找文件描述符是否在unordered_map中
        if(it == _channels.end())//没找到
        {
            return false;
        }
        return true;
    }
private:    
    int _epfd;//epoll 实例的文件描述符
    struct epoll_event _evs[maxEpollEvents];//epoll数组
    std::unordered_map<int, Channel*> _channels;
};

